package controllers

import (
	"context"
	"errors"
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/logs"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/lease"
	"gitverse.ru/synapse/kubelatte/pkg/operator/controllers/mocks"
	"go.uber.org/zap"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	controllerruntime "sigs.k8s.io/controller-runtime"
	"sync"
	"testing"
)

func Test_updateRegoStatus(t *testing.T) {
	type args struct {
		template v1alpha1.Template
	}
	tests := []struct {
		name string
		args args
		want v1alpha1.Template
	}{
		{
			name: "remove fail status",
			args: args{
				template: v1alpha1.Template{
					TypeMeta:   metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{},
					Spec: v1alpha1.TemplateSpec{
						Data: "import future.keywords.contains\n    import future.keywords.if\n    import future.keywords.in\n\n    violation contains {\"msg\": msg} if {\n      params := object.get(input, \"parameters\", {})\n      name := object.get(params, \"name\", [])\n      objName := input.review.name\n      objName == name\n      msg := sprintf(\"Creating this pod is prohibited! Do not create %v\", [objName])\n    }",
						Type: "rego",
					},
					Status: v1alpha1.TemplateStatus{
						Phase: "",
						Conditions: []v1alpha1.Condition{{
							Type:               RegoRulePrecompileFailed,
							Status:             "",
							LastTransitionTime: metav1.Time{},
							Message:            "",
						}},
					},
				},
			},
			want: v1alpha1.Template{TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{},
				Spec: v1alpha1.TemplateSpec{
					Data: "import future.keywords.contains\n    import future.keywords.if\n    import future.keywords.in\n\n    violation contains {\"msg\": msg} if {\n      params := object.get(input, \"parameters\", {})\n      name := object.get(params, \"name\", [])\n      objName := input.review.name\n      objName == name\n      msg := sprintf(\"Creating this pod is prohibited! Do not create %v\", [objName])\n    }",
					Type: "rego",
				},
				Status: v1alpha1.TemplateStatus{
					Phase:      v1alpha1.Running,
					Conditions: []v1alpha1.Condition{},
				},
			},
		},
		{
			name: "add fail status",
			args: args{
				template: v1alpha1.Template{
					TypeMeta:   metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{},
					Spec: v1alpha1.TemplateSpec{
						Data: "import future.keywords.contains\n    import future.keywords.if\n\n    violation contains {\"msg\": msg} if {\n      objName := http.send({\"method\": \"get\", \"url\": \"http://127.0.0.1:17773/relconf\"})\n      msg := sprintf(\"HTTP is denied. Here is responce: '%v'\", [objName])\n    }",
						Type: "rego",
					},
					Status: v1alpha1.TemplateStatus{
						Phase:      "",
						Conditions: []v1alpha1.Condition{},
					},
				},
			},
			want: v1alpha1.Template{TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{},
				Spec: v1alpha1.TemplateSpec{
					Data: "import future.keywords.contains\n    import future.keywords.if\n\n    violation contains {\"msg\": msg} if {\n      objName := http.send({\"method\": \"get\", \"url\": \"http://127.0.0.1:17773/relconf\"})\n      msg := sprintf(\"HTTP is denied. Here is responce: '%v'\", [objName])\n    }",
					Type: "rego",
				},
				Status: v1alpha1.TemplateStatus{
					Phase: v1alpha1.Failed,
					Conditions: []v1alpha1.Condition{
						{
							Type:               RegoRulePrecompileFailed,
							LastTransitionTime: metav1.Now(),
							Status:             "True",
							Message:            "[OpaManager] Precompile failed %s: 1 error occurred: kubelatte.rego:6: rego_type_error: undefined function http.send",
						},
					},
				},
			},
		},
		{
			name: "update fail status",
			args: args{
				template: v1alpha1.Template{
					TypeMeta:   metav1.TypeMeta{},
					ObjectMeta: metav1.ObjectMeta{},
					Spec: v1alpha1.TemplateSpec{
						Data: "import future.keywords.contains\n    import future.keywords.if\n\n    violation contains {\"msg\": msg} if {\n      objName := http.send({\"method\": \"get\", \"url\": \"http://127.0.0.1:17773/relconf\"})\n      msg := sprintf(\"HTTP is denied. Here is responce: '%v'\", [objName])\n    }",
						Type: "rego",
					},
					Status: v1alpha1.TemplateStatus{
						Phase: "",
						Conditions: []v1alpha1.Condition{
							{
								Type:               RegoRulePrecompileFailed,
								Status:             "",
								LastTransitionTime: metav1.Time{},
								Message:            "",
							},
						},
					},
				},
			},
			want: v1alpha1.Template{TypeMeta: metav1.TypeMeta{},
				ObjectMeta: metav1.ObjectMeta{},
				Spec: v1alpha1.TemplateSpec{
					Data: "import future.keywords.contains\n    import future.keywords.if\n\n    violation contains {\"msg\": msg} if {\n      objName := http.send({\"method\": \"get\", \"url\": \"http://127.0.0.1:17773/relconf\"})\n      msg := sprintf(\"HTTP is denied. Here is responce: '%v'\", [objName])\n    }",
					Type: "rego",
				},
				Status: v1alpha1.TemplateStatus{
					Phase: v1alpha1.Failed,
					Conditions: []v1alpha1.Condition{
						{
							Type:               RegoRulePrecompileFailed,
							LastTransitionTime: metav1.Now(),
							Status:             "True",
							Message:            "[OpaManager] Precompile failed %s: 1 error occurred: kubelatte.rego:6: rego_type_error: undefined function http.send",
						},
					},
				},
			},
		},
	}

	t.Run(tests[0].name, func(t *testing.T) {
		assert.Equalf(t, tests[0].want, updateRegoStatus(tests[0].args.template), "updateRegoStatus(%v)", tests[0].args.template)
	})

	t.Run(tests[1].name, func(t *testing.T) {
		got := updateRegoStatus(tests[1].args.template)
		tests[1].want.Status.Conditions[0].LastTransitionTime = got.Status.Conditions[0].LastTransitionTime
		assert.Equalf(t, tests[1].want, got, "updateRegoStatus(%v)", tests[1].args.template)
	})
	t.Run(tests[2].name, func(t *testing.T) {
		got := updateRegoStatus(tests[2].args.template)
		tests[2].want.Status.Conditions[0].LastTransitionTime = got.Status.Conditions[0].LastTransitionTime
		assert.Equalf(t, tests[2].want, got, "updateRegoStatus(%v)", tests[2].args.template)
	})
}

func TestTemplateReconciler_HealthCheck(t *testing.T) {
	t.Run("test", func(t *testing.T) {
		r := &TemplateReconciler{
			initedFlag: false,
		}
		assert.Equalf(t, false, r.HealthCheck(), "HealthCheck()")
	})
}

func TestTemplateReconciler_deferLog(t *testing.T) {
	type args struct {
		req        controllerruntime.Request
		configData []byte
		err        error
	}
	tests := []struct {
		name string
		args args
	}{
		{
			name: "err nil",
			args: args{
				req:        controllerruntime.Request{},
				configData: nil,
				err:        nil,
			},
		},
		{
			name: "err not nil",
			args: args{
				req:        controllerruntime.Request{},
				configData: nil,
				err:        errors.New("error"),
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logs.Logger = zap.NewNop()
			r := &TemplateReconciler{
				Client:                  nil,
				Scheme:                  nil,
				once:                    sync.Once{},
				templateObj:             v1alpha1.Template{},
				deletedTemplateObj:      v1alpha1.Template{},
				initedFlag:              false,
				counterInstancesAtStart: 0,
			}
			r.deferLog(tt.args.req, tt.args.configData, tt.args.err)
		})
	}
}

func TestTemplateReconciler_updateStatus(t *testing.T) {
	lease.SetMeLeader()
	logs.Logger = zap.NewNop()

	template := v1alpha1.Template{Status: v1alpha1.TemplateStatus{
		Conditions: []v1alpha1.Condition{
			{Type: v1alpha1.LastHandledHash,
				Message: "{\"type\":\"pod\",\"items\":null}"},
		},
	}}

	var isAudit bool
	isAudit = true

	mClient := mocks.NewClient(t)
	mStatusWriter := mocks.NewSubResourceWriter(t)
	mStatusWriter.On("Update", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("List", mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
	mClient.On("Status").Return(mStatusWriter).Maybe()

	tr := TemplateReconciler{Client: mClient}

	res, err := tr.updateStatus(context.Background(), template, isAudit)
	if err != nil {
		t.Fatal("error", err)
	}
	fmt.Println(res)
}
